/*
 * Line2D.h
 *
 *  Created on: 2009/01/23
 *      Author: Yongshik Cho
 */

#ifndef LINE2D_H_
#define LINE2D_H_

#include <cmath>

#ifndef COLLIDABLE_H_
#	include "../active/Collidable.h"
#endif

class Line2D : public Collidable
{
public:
	Line2D(uint32 type = Collidable::COLLIDABLE_LINE) :
		Collidable(type)
	{
		mPoints.clear();
		mPoints.push_back(Vector2D(0.f, 0.f));
		mPoints.push_back(Vector2D(0.f, 0.f));

		mClosed = false;
	}
	Line2D(const Vector2D& p1, const Vector2D& p2, uint32 type = Collidable::COLLIDABLE_LINE) :
		Collidable(type)
	{
		mPoints.clear();
		mPoints.push_back(p1);
		mPoints.push_back(p2);

		mClosed = false;
	}

	Line2D(const Line2D& rhs, uint32 type = Collidable::COLLIDABLE_LINE) :
		Collidable(type)
	{
		mPoints.clear();
		mPoints.push_back(rhs.mPoints[0]);
		mPoints.push_back(rhs.mPoints[1]);

		mClosed = false;
	}

	virtual ~Line2D() {}

public:
	inline const bool in(const Vector2D& p) const
	{
		if((Line2D::crossProduct(mPoints[0], mPoints[1], p) == 0) &&
			Line2D::isOnTheSegment(mPoints[0], mPoints[1], p))
			return true;

		return false;
	}

	inline const bool intersects(const Line2D& rhs) const
	{
		float d1 = Line2D::crossProduct(rhs.mPoints[0], rhs.mPoints[1], mPoints[0]);
		float d2 = Line2D::crossProduct(rhs.mPoints[0], rhs.mPoints[1], mPoints[1]);
		float d3 = Line2D::crossProduct(mPoints[0], mPoints[1], rhs.mPoints[0]);
		float d4 = Line2D::crossProduct(mPoints[0], mPoints[1], rhs.mPoints[1]);

		if (((d1>0 && d2<0) || (d1<0 && d2>0)) && (((d3>0 && d4<0) || (d3<0 && d4>0))))
		{
			return true;
		}
		else if((d1 == 0) && Line2D::isOnTheSegment(rhs.mPoints[0], rhs.mPoints[1], mPoints[0]))
		{
			return true;
		}
		else if((d2 == 0) && Line2D::isOnTheSegment(rhs.mPoints[0], rhs.mPoints[1], mPoints[1]))
		{
			return true;
		}
		else if((d3 == 0) && Line2D::isOnTheSegment(mPoints[0], mPoints[1], rhs.mPoints[0]))
		{
			return true;
		}
		else if((d4 == 0) && Line2D::isOnTheSegment(mPoints[0], mPoints[1], rhs.mPoints[1]))
		{
			return true;
		}
		else
		{
			return false;
		}

		return false;
	}


	inline const Line2D& operator =  (const Line2D& rhs)
	{
		mPoints[0] = rhs.mPoints[0];
		mPoints[1] = rhs.mPoints[1];
		return *this;
	}

	inline Line2D operator +  (const Vector2D& rhs) const
	{
		return Line2D(mPoints[0] + rhs, mPoints[1] + rhs);
	}
	inline const Line2D& operator += (const Vector2D& rhs)
	{
		mPoints[0] += rhs;
		mPoints[1] += rhs;
		return *this;
	}
	inline Line2D operator -  (const Vector2D& rhs) const
	{
		return Line2D(mPoints[0] - rhs, mPoints[1] - rhs);
	}
	inline const Line2D& operator -= (const Vector2D& rhs)
	{
		mPoints[0] -= rhs;
		mPoints[1] -= rhs;
		return *this;
	}

	inline bool operator == (const Line2D& rhs) const
	{
		return (mPoints[0] == rhs.mPoints[0] && mPoints[1] == rhs.mPoints[1]);
	}
	inline bool operator != (const Line2D& rhs) const
	{
		return !(mPoints[0] == rhs.mPoints[0] && mPoints[1] == rhs.mPoints[1]);
	}

	static bool isOnTheSegment(const Vector2D& pi, const Vector2D& pj, const Vector2D& pk)
	{
#ifdef _MSC_VER
		if(min(pi.x,pj.x) <= pk.x && pk.x <= max(pi.x,pj.x) && min(pi.y,pj.y) <= pk.y && pk.y <= max(pi.y, pj.y))
			return true;
#else
		if(std::min(pi.x,pj.x) <= pk.x && pk.x <= std::max(pi.x,pj.x) && std::min(pi.y,pj.y) <= pk.y && pk.y <= std::max(pi.y, pj.y))
			return true;
#endif

		return false;
	}

private:
	static float crossProduct(const Vector2D& pi, const Vector2D& pj, const Vector2D& pk)
	{
		return (((pk.x-pi.x)*(pj.y-pi.y))-((pj.x-pi.x)*(pk.y-pi.y)));
	}

};

#endif /* LINE2D_H_ */
